odhcp6c: apply draft-ietf-6man-slaac-renum-11 lifetime rules
authorJonas Lochmann <[email protected]>
Mon, 3 Nov 2025 00:00:00 +0000 (01:00 +0100)
committerÁlvaro Fernández Rojas <[email protected]>
Mon, 17 Nov 2025 07:57:45 +0000 (08:57 +0100)
Instead of applying the complicated ruleset from RFC 4862,
just use the new address information as described in the draft
draft-ietf-6man-slaac-renum-11.

Signed-off-by: Jonas Lochmann <[email protected]>
Link: https://github.com/openwrt/odhcp6c/pull/114
Signed-off-by: Álvaro Fernández Rojas <[email protected]>
src/dhcpv6.c
src/odhcp6c.c
src/odhcp6c.h
src/ra.c

index 7f73699141dab9f86fe41d79e0bfe3f9715ea6c6..872f169e67453c78fc2b4898df58b1c50d9ee07b 100644 (file)
@@ -1742,7 +1742,7 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end, int *ret)
                        }
 
                        if (update_state) {
-                               if (odhcp6c_update_entry(STATE_IA_PD, &entry, 0, 0))
+                               if (odhcp6c_update_entry(STATE_IA_PD, &entry, 0))
                                        updated_IAs++;
 
                                syslog(LOG_INFO, "%s/%d preferred %d valid %d",
@@ -1797,7 +1797,7 @@ static unsigned int dhcpv6_parse_ia(void *opt, void *end, int *ret)
                        }
 
                        if (update_state) {
-                               if (odhcp6c_update_entry(STATE_IA_NA, &entry, 0, 0))
+                               if (odhcp6c_update_entry(STATE_IA_NA, &entry, 0))
                                        updated_IAs++;
 
                                syslog(LOG_INFO, "%s preferred %d valid %d",
index 8b880e30cd98a1b86a5a4fa11986569d2249c8a0..7b1a387c04ad5d2a81d50434c84f127d0115bc5e 100644 (file)
@@ -939,64 +939,20 @@ static struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const
 }
 
 bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new,
-               uint32_t safe, unsigned int holdoff_interval)
+               unsigned int holdoff_interval)
 {
        struct odhcp6c_entry *x = odhcp6c_find_entry(state, new);
-       uint32_t new_valid;
-
-       /*
-        * "safe" refers to https://www.rfc-editor.org/rfc/rfc4862#section-5.5.3
-        * section e; it is either the two hours in seconds or zero when (e)
-        * does not apply.
-        *
-        * The base condition for applying safe is that there is already a
-        * matching prefix (and safe itself must be set).
-        */
-       if (safe && x) {
-               if (new->valid > safe || new->valid > x->valid) {
-                       /*
-                        * 1: If the received Valid Lifetime is greater than 2 hours or
-                        * greater than RemainingLifetime, set the valid lifetime of the
-                        * corresponding address to the advertised Valid Lifetime.
-                        */
-                       new_valid = new->valid;
-               } else if (x->valid <= safe) {
-                       /*
-                        * 2: If RemainingLifetime is less than or equal
-                        * to 2 hours, ignore the Prefix Information option with
-                        * regards to the valid lifetime, unless the Router
-                        * Advertisement from which this option was obtained has
-                        * been authenticated (e.g., via Secure Neighbor
-                        * Discovery [RFC3971]).  If the Router Advertisement
-                        * was authenticated, the valid lifetime of the
-                        * corresponding address should be set to the Valid
-                        * Lifetime in the received option.
-                        *
-                        * Since authenticated advertisements aren't supported we
-                        * always keep the old value.
-                        */
-                       new_valid = x->valid;
-               } else {
-                       /*
-                        * 3: Otherwise, reset the valid lifetime of the corresponding
-                        * address to 2 hours.
-                        */
-                       new_valid = safe;
-               }
-       } else {
-               new_valid = new->valid;
-       }
 
        if (x) {
-               if (holdoff_interval && new_valid >= x->valid &&
-                               new_valid != UINT32_MAX &&
-                               new_valid - x->valid < holdoff_interval &&
+               if (holdoff_interval && new->valid >= x->valid &&
+                               new->valid != UINT32_MAX &&
+                               new->valid - x->valid < holdoff_interval &&
                                new->preferred >= x->preferred &&
                                new->preferred != UINT32_MAX &&
                                new->preferred - x->preferred < holdoff_interval)
                        return false;
 
-               x->valid = new_valid;
+               x->valid = new->valid;
                x->preferred = new->preferred;
                x->t1 = new->t1;
                x->t2 = new->t2;
index 42c645d1ed363fd4fdab415be0c896012aaa6bd7..b229412c65371b97b015167859c371cb242bd22b 100644 (file)
@@ -576,7 +576,7 @@ void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len);
 
 // Entry manipulation
 bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new,
-                               uint32_t safe, unsigned int holdoff_interval);
+                               unsigned int holdoff_interval);
 
 void odhcp6c_expire(bool expire_ia_pd);
 uint32_t odhcp6c_elapsed(void);
index dcc5fd6c470cd4fb0ad3c9982a26a9b94833aeae..ca9e691449d689134a87d8314bfce6a757398d1a 100644 (file)
--- a/src/ra.c
+++ b/src/ra.c
@@ -437,7 +437,7 @@ bool ra_process(void)
                entry->valid = router_valid;
                entry->preferred = entry->valid;
                changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry,
-                                               0, ra_holdoff_interval);
+                                               ra_holdoff_interval);
 
                // Parse hop limit
                changed |= ra_set_hoplimit(adv->nd_ra_curhoplimit);
@@ -478,8 +478,18 @@ bool ra_process(void)
 
                                if (entry->priority > 0)
                                        changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry,
-                                                                       0, ra_holdoff_interval);
+                                                                       ra_holdoff_interval);
                        } else if (opt->type == ND_OPT_PREFIX_INFORMATION && opt->len == 4) {
+                               /*
+                                * We implement draft-ietf-6man-slaac-renum-11 here:
+                                * https://datatracker.ietf.org/doc/html/draft-ietf-6man-slaac-renum-11#section-5.4
+                                *
+                                * This removes the two hour magic and instead just uses the new
+                                * data. If the lifetime is zero, then the prefix is removed.
+                                *
+                                * An entry with lifetime zero is added. odhcp6c_expire will remove
+                                * it again. odhcp6c_expire is called at the end of this function.
+                                */
                                struct nd_opt_prefix_info *pinfo = (struct nd_opt_prefix_info*)opt;
                                entry->router = any;
                                entry->target = pinfo->nd_opt_pi_prefix;
@@ -497,7 +507,7 @@ bool ra_process(void)
                                if ((pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
                                    !ptp_link)
                                        changed |= odhcp6c_update_entry(STATE_RA_ROUTE, entry,
-                                                                       7200, ra_holdoff_interval);
+                                                                       ra_holdoff_interval);
 
                                if (!(pinfo->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) ||
                                                pinfo->nd_opt_pi_prefix_len != 64)
@@ -507,7 +517,7 @@ bool ra_process(void)
                                entry->target.s6_addr32[3] = lladdr.s6_addr32[3];
 
                                changed |= odhcp6c_update_entry(STATE_RA_PREFIX, entry,
-                                                               7200, ra_holdoff_interval);
+                                                               ra_holdoff_interval);
                        } else if (opt->type == ND_OPT_RECURSIVE_DNS && opt->len > 2) {
                                entry->router = from.sin6_addr;
                                entry->priority = 0;
@@ -520,7 +530,7 @@ bool ra_process(void)
                                        memcpy(&entry->target, &opt->data[6 + i * sizeof(entry->target)],
                                                        sizeof(entry->target));
                                        changed |= odhcp6c_update_entry(STATE_RA_DNS, entry,
-                                                                       0, ra_holdoff_interval);
+                                                                       ra_holdoff_interval);
                                }
                        } else if (opt->type == ND_OPT_DNSSL && opt->len > 1) {
                                uint32_t *valid = (uint32_t*)&opt->data[2];
@@ -542,7 +552,7 @@ bool ra_process(void)
                                                continue;
 
                                        changed |= odhcp6c_update_entry(STATE_RA_SEARCH, entry,
-                                                                       0, ra_holdoff_interval);
+                                                                       ra_holdoff_interval);
                                        entry->auxlen = 0;
                                }
                        }